package com.nodecollege.cloud.client.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.nodecollege.cloud.client.feign.ApiClient;
import com.nodecollege.cloud.client.feign.ApiVisitClient;
import com.nodecollege.cloud.client.service.ApiService;
import com.nodecollege.cloud.common.UpmsConstants;
import com.nodecollege.cloud.common.annotation.ApiAnnotation;
import com.nodecollege.cloud.common.aop.NCAop;
import com.nodecollege.cloud.common.model.NCResult;
import com.nodecollege.cloud.common.model.po.UpmsApi;
import com.nodecollege.cloud.common.model.po.UpmsApiVisit;
import com.nodecollege.cloud.common.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.PostConstruct;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author LC
 * @date 2020/1/10 10:19
 */
@Slf4j
@Service
public class ApiServiceImpl implements ApiService {

    @Value("${spring.application.name:default}")
    private String applicationName;

    private List<UpmsApi> apiList = new ArrayList<>(10000);

    private Map<UpmsApiVisit, AtomicLong> visitMap = new ConcurrentHashMap<>(10000);

    @Autowired
    private Environment environment;

    @Autowired
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    @Autowired
    private ApiClient apiClient;

    @Autowired
    private ApiVisitClient apiVisitClient;

    /**
     * 项目启动时，自动查询用注解标记过的接口数据，并和数据库进行比对，有则更新，无则插入
     */
    @PostConstruct
    private synchronized void initApiList() {
        log.info("初始化查询api信息！");
        if (!apiList.isEmpty()) {
            return;
        }
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
        List<UpmsApi> apis = new ArrayList<>((int) Math.ceil(handlerMethods.size() / 0.75) + 1);
        for (Map.Entry<RequestMappingInfo, HandlerMethod> m : handlerMethods.entrySet()) {
            RequestMappingInfo info = m.getKey();
            HandlerMethod method = m.getValue();
            ApiAnnotation apiAnnotation = method.getMethodAnnotation(ApiAnnotation.class);
            UpmsApi api = new UpmsApi();
            api.setApplicationName(applicationName);
            api.setControllerName(NCAop.getConciseMethodName(method.getMethod().getDeclaringClass().getName()));
            api.setApiUrl(info.getPatternsCondition().getPatterns().iterator().next());
            if (apiAnnotation != null) {
                api.setModularName(apiAnnotation.modularName());
                api.setDescription(apiAnnotation.description());
                api.setState(apiAnnotation.state());
            } else {
                api.setModularName(api.getControllerName());
                api.setDescription(api.getApiUrl());
                api.setState(UpmsConstants.API_NORMAL);
            }
            log.info("api信息 {}", api.toString());
            apis.add(api);
        }
        NCResult<UpmsApi> result = apiClient.initApiList(apis);
        if (result.getSuccess()) {
            apiList = new ArrayList<>(result.getRows());
        }
    }

    /**
     * 添加接口访问记录
     *
     * @param upmsApiVisit
     */
    @Override
    public void addApiVisitCache(UpmsApiVisit upmsApiVisit) {
        log.debug("接口访问记录 {}", JSONObject.toJSONString(upmsApiVisit));
        // 1. 查出接口id
        if (apiList.isEmpty()) {
            initApiList();
        }
        for (int i = 0; i < apiList.size(); i++) {
            if (apiList.get(i).getApplicationName().equals(upmsApiVisit.getApplicationName())
                    && apiList.get(i).getControllerName().equals(upmsApiVisit.getControllerName())
                    && apiList.get(i).getApiUrl().equals(upmsApiVisit.getApiUrl())) {
                // 设置接口id
                upmsApiVisit.setApiId(apiList.get(i).getApiId());
                break;
            }
        }
        upmsApiVisit.setVisitDate(DateUtils.formatDate(new Date(), DateUtils.DATE_PATTERN.YYYYMMDD));
        upmsApiVisit.setVisitH(DateUtils.formatDate(new Date(), DateUtils.DATE_PATTERN.HH));
        InetAddress localHost = null;
        try {
            localHost = Inet4Address.getLocalHost();
        } catch (UnknownHostException e) {
            log.error(e.getMessage(), e);
        }
        upmsApiVisit.setLocalIpPort(localHost.getHostAddress() + ":" + environment.getProperty("local.server.port"));

        if (!visitMap.containsKey(upmsApiVisit)) {
            visitMap.put(upmsApiVisit, new AtomicLong(0));
        }
        visitMap.get(upmsApiVisit).getAndIncrement();
    }

    /**
     * 每小时第一分钟的时候将上一个小时的数据保存到数据库中
     */
    @Scheduled(cron = "0 1 0/1 * * ? ")
    public void sync() {
        log.info("定时任务！！！！");
        Calendar now = Calendar.getInstance();
        now.setTime(new Date());
        now.add(Calendar.HOUR, -1);
        String dateH = DateUtils.formatDate(now.getTime(), DateUtils.DATE_PATTERN.YEAR_HOUR_FORMAT);

        int initialCapacity = (int) Math.ceil(visitMap.size() / 0.75) + 1;
        List<UpmsApiVisit> oldList = new ArrayList<>(initialCapacity);
        Map<UpmsApiVisit, AtomicLong> nowMap = new ConcurrentHashMap(initialCapacity);
        for (Map.Entry<UpmsApiVisit, AtomicLong> entry : visitMap.entrySet()) {
            if (entry.getKey().getVisitH().equals(dateH)) {
                entry.getKey().setVisitCount(entry.getValue().get());
                oldList.add(entry.getKey());
            } else {
                nowMap.put(entry.getKey(), entry.getValue());
            }
        }
        visitMap = nowMap;
        apiVisitClient.addApiVisitList(oldList);
        log.info("#############   自动保存上一个小时的访问记录任务  结束   #############");
    }
}
